
/*
 * Chain.java
 *
 * Created on July 29, 2006, 8:49 PM
 *
 */
package galronnlp.perceptron;

//~--- non-JDK imports --------------------------------------------------------

import galronnlp.util.Symbol;
import galronnlp.util.SymbolGenerator;

//~--- JDK imports ------------------------------------------------------------

import java.util.Iterator;
import java.util.LinkedList;

//~--- classes ----------------------------------------------------------------

/**
 *
 * A chain is a <code>LinkedList</code> of <code>Symbol</code>, used in the <code>Grammar</code>
 * class to represent the allowable chains.
  *
 * @author Daniel A. Galron
 */

//I chose this representation to allow for easy appending when constructing 
//chains from context free productions, because when constructing chains we
//need to append symbols to the very end; and to do so for chains of Nodes
//would be expensive, since I don't have a pointer to the end of a Node chain
//(i.e. a (partial) tree of Nodes).
public class Chain extends LinkedList<Symbol> {
    public Chain() {
        super();
    }

    public Chain(Chain c) {
        super(c);
    }

    /** Creates a new instance of Chain */
    public Chain(Symbol s) {
        super();
        this.addLast(s);
    }

    //~--- methods ------------------------------------------------------------

    public void append(Chain c) {
        super.addAll(c);
    }

    public void append(Symbol s) {
        super.addLast(s);
    }

    /**
     * Builds a tree representation of this chain.
     *
     * @param parameters A paramater vector, used for scoring the tree representation
     * @param gen The <code>SymbolGenerator</code> used to enumerate the symbols in the tree
     * @param lookAhead The look ahead token (used as a feature in the tree)
     * @param lookAheadPOS The part of speech tag of the look ahead token (used as a feature in the tree)
     * @param rules The object containing the lexical head finding rules, used in firing the features in the tree.
     *
     * @return the tree representation of the chain.
     */
    public Node buildNodeTree(FeatureVector parameters, SymbolGenerator gen,
                              Symbol lookAhead, Symbol lookAheadPOS,
                              HeadFinderRules rules) {
        Node ret = null;

        // In the perceptron parser, I would never be here. However, for the sake
        // of completeness, I'm allowing for the possibility of having a chain of
        // size 1
        if (this.size() == 1) {
            ret = new Node(null, Node.NONTERMINAL, this.getHead(), lookAhead,
                           lookAheadPOS, gen, parameters);
        }

        // If this chain only contains two elements, that is, if it is a lexical entry and its POS,
        else if (this.size() == 2) {
            ret = new Node(null, Node.POS, this.getHead(), lookAhead,
                           lookAheadPOS, gen, parameters);
            ret.addChild(new Node(ret, Node.TERMINAL, this.getTail(),
                                  lookAhead, lookAheadPOS, gen, parameters));
        } else if (this.size() > 2) {

            // Create the root of the new tree
            ret = new Node(null, Node.NONTERMINAL, this.getHead(), lookAhead,
                           lookAheadPOS, gen, parameters);

            Node parent = ret;

            // Iterate through all nodes in the chain, and add them as non-terminals to the end of the tree-chain
            for (int i = 1; i < super.size() - 2; i++) {
                parent.addChild(new Node(parent, Node.NONTERMINAL,
                                         super.get(i), lookAhead,
                                         lookAheadPOS, gen, parameters));
                parent = parent.getChildren().getFirst();
            }

            // add the lexical category as a POS
            parent.addChild(new Node(parent, Node.POS,
                                     super.get(super.size() - 2), lookAhead,
                                     lookAheadPOS, gen, parameters));
            parent = parent.getChildren().getFirst();

            // finally, add the last element as a terminal node.
            parent.addChild(new Node(parent, Node.TERMINAL, this.getTail(),
                                     lookAhead, lookAheadPOS, gen,
                                     parameters));
        } else {
            System.err.println("ERROR! I SHOULDN'T BE HERE!");
            System.exit(1);
        }

        ret.fireFeatures(rules);

        return ret;
    }

    public Chain copy() {
        return new Chain(this);
    }

    public boolean equals(Object o) {
        Chain c = (Chain) o;

        if (c.size() != this.size()) {
            return false;
        }

        boolean ret = true;

        for (int i = 0; i < this.size(); i++) {
            ret &= this.get(i).equals(c.get(i));
        }

        return ret;
    }

    public double hashcode() {
        return this.getHead().hashCode() * this.getTail().hashCode()
               * this.size();
    }

    public String toString() {
        String ret = this.get(0).toString();

        for (int i = 1; i < this.size(); i++) {
            ret += " --> " + this.get(i);
        }

        ret += "\n";

        return ret;
    }

    //~--- get methods --------------------------------------------------------

    public Symbol getHead() {
        return super.getFirst();
    }

    public Symbol getTail() {
        return super.getLast();
    }

    public String getXMLRep() {
        String ret = "<chain>\n";

        for (Iterator<Symbol> it = this.iterator(); it.hasNext(); ) {
            Symbol next = it.next();

            // This is needed because & is a special symbol, and must be represented
            // as &amp; in an XML document
            if (next.toString().equals("&")) {
                ret += "\t<node label=\"&amp;\"/>\n";
            } else {
                ret += "\t<node label=\"" + next + "\"/>\n";
            }
        }

        ret += "</chain>\n";

        return ret;
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
